#include "codec.h"

#include "muduo/base/Logging.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"

#include <set>
#include <stdio.h>
#include <unistd.h>

using namespace muduo;
using namespace muduo::net;

class ChatServer : noncopyable
{
public:
  // 这里有几点值得注意，在以往的代码里是直接把本class的`onMessage()`注册给`server_`;
  // 这里我们把`LengthHeaderCodec::onMessage()`注册给`server_`，然后向`codec_`注册了`ChatServer::onStringMessage()`，等于说让`codec_`负责解析消息，然后把完整的消息回调给`ChatServer`。
  // 这正是我前面提到的“一个简单的间接层”​，在不增加muduo库的复杂度的前提下，提供了足够的灵活性让我们在用户代码里完成需要的工作。
  ChatServer(EventLoop* loop,
             const InetAddress& listenAddr)
  : server_(loop, listenAddr, "ChatServer"),
    codec_(std::bind(&ChatServer::onStringMessage, this, _1, _2, _3))
  { // 在构造函数里注册回调
    // AI: 在单线程上下文、且事件循环尚未启动之前，这种注册方式是安全的
    server_.setConnectionCallback(
        std::bind(&ChatServer::onConnection, this, _1));
    server_.setMessageCallback(
        std::bind(&LengthHeaderCodec::onMessage, &codec_, _1, _2, _3));
  }

  // server_.start()绝对不能在构造函数里调用，这么做将来会有线程安全的问题，见§ 1.2的论述
  // 1.2:
  // 对象构造要做到线程安全，唯一的要求是在构造期间不要泄露this指针，即
  // - 不要在构造函数中注册任何回调；
  // - 也不要在构造函数中把this传给跨线程的对象；
  // - 即便在构造函数的最后一行也不行。
  // 之所以这样规定，是因为在构造函数执行期间对象还没有完成初始化，如果this被泄露(escape)给了其他对象（其自身创建的子对象除外）​，那么别的线程有可能访问这个半成品对象，这会造成难以预料的后果。
  void start()
  {
    server_.start(); 
  }

private:
  // 以下是处理连接的建立和断开的代码，注意它把新建的连接加入到connections_容器中，把已断开的连接从容器中删除。
  // 这么做是为了避免内存和资源泄漏，TcpConnectionPtr是boost::shared_ptr<TcpConnection>，是muduo里唯一一个默认采用shared_ptr来管理生命期的对象。§ 4.7谈了这么做的原因。
  void onConnection(const TcpConnectionPtr& conn)
  {
    LOG_INFO << conn->peerAddress().toIpPort() << " -> "
             << conn->localAddress().toIpPort() << " is "
             << (conn->connected() ? "UP" : "DOWN");

    if (conn->connected())
    {
      connections_.insert(conn);
    }
    else
    {
      connections_.erase(conn);
    }
  }

  // 在收到消息之后，服务器会遍历整个容器，把消息广播给其中的每一个TCP连接
  // 以下是服务端处理消息的代码，它遍历整个connections_容器，把消息打包发送给各个客户连接。
  void onStringMessage(const TcpConnectionPtr&,
                       const string& message,
                       Timestamp) 
  {
    for (ConnectionList::iterator it = connections_.begin();
        it != connections_.end();
        ++it)
    {
      codec_.send(get_pointer(*it), message);
    }
  }

  typedef std::set<TcpConnectionPtr> ConnectionList;
  TcpServer server_; // 除了经常见到的EventLoop和TcpServer，ChatServer还定义了codec_和connections_作为成员，后者存放目前已建立的客户连接
  LengthHeaderCodec codec_;
  ConnectionList connections_;
};

int main(int argc, char* argv[])
{
  LOG_INFO << "pid = " << getpid();
  if (argc > 1)
  {
    EventLoop loop;
    uint16_t port = static_cast<uint16_t>(atoi(argv[1]));
    InetAddress serverAddr(port);
    ChatServer server(&loop, serverAddr);
    server.start();
    loop.loop();
  }
  else
  {
    printf("Usage: %s port\n", argv[0]);
  }
}

